#include "Ia.h"
/*
#include "../UCT/arbreUCT.h"
#include "../UCT/listeChaineeUCT.h"
*/

int nbd; //variable servant à communiquer simplement avec la fonction threadé.

Ia::Ia(EPlateau * plateau, int nombreThread)
{
        cout<<"Construction d'une IA de "<<nombreThread<<" threads"<<endl;
        cout<<plateau->getTaille()<<endl;
        Coord cTemp;
        listeChainee.resize(plateau->getTaille());
        cout<<"boucle"<<endl;
        for(int i =0; i<plateau->getTaille(); i++){
            listeChainee[i].resize(plateau->getTaille());
        }
        cout<<"ok!"<<endl;
        for(int i = 0; i<plateau->getLpo().size(); i++){
            cout<<plateau->getLpo()[i].p<<endl;
        }
        for(int i = 0; i < plateau->getTaille(); i++){
            for(int j = 0; j<plateau->getTaille(); j++){
                cTemp.x = i;
                cTemp.y = j;
                listeChainee[i][j].initialisateur(plateau->getLpo(), cTemp );
            }
        }

        cout<<"etape 1 faite!"<<endl;

        p=plateau;
        nbThread = nombreThread;
        thread.resize(nombreThread); //déclaration des threads

        tableMutex.resize(p->getTaille()*p->getTaille()); //déclaration des mutexs
        for (int i=0;i<tableMutex.size();i++) {
         // tableMutex[i]=PTHREAD_MUTEX_INITIALIZER; //tableMutex protege les differentes branches de l'arbres.
          pthread_mutex_init (&tableMutex[i], NULL);
        }
        Mutex.resize(2); //protege les vecteur RAVE.
        for (int i=0;i<Mutex.size();i++) {
           //Mutex[i]=PTHREAD_MUTEX_INITIALIZER;
		   pthread_mutex_init (&Mutex[i], NULL);
        }


        Poids.resize(p->getTaille());
        // PoidsFormes.resize(gb->getTaille());
        for (int i = 0; i < p->getTaille(); i++) {
                Poids[i].resize(p->getTaille());
        }
        reinittable(&Poids);

        //mesRegles = new regles(gb, this); //initialisation du systeme expert, à remetre dans p)eu de temps.


        cout<<"UCT..."<<endl;
        // -------------------------------- UCT --------------------------------

        //construction des listes Chainées:
        trickRave();
/*
        cout<<"ETAT des listes: "<<endl;
        listeChaineeBlanc.afficherListeMaillons();
        cout<<"et noir : "<<endl;
        listeChaineeNoir.afficherListeMaillons();
        cout<<"fin etat des listes."<<endl;
        */
        cout<<nbThread<<endl;
        for (int i = 0; i < nbThread; i++) {
            cout<<"je construit un arbre..."<<endl;
            arbres.push_back(new arbreUCT(p,&tableMutex,&Mutex,&listeChainee));
        }

        for (int i =1; i< arbres.size(); i++) {
            arbres[i]->setNoeudRacine(arbres[0]->getNoeudRacine());
        }
        p = plateau;

        nbRepet = NB_DESCENTES; //valeur test : Pour le nomb re de descentes totale, ca fera nbRepet x nbThread

        nbd = nbRepet;
        poidsUCT = POIDS_UCT;
        poidsDicoFormes = POIDS_FORMES;
        cout<<"Ia construite!"<<endl;
}

char Ia::getCouleur(){
    return couleur;
}


Dico * Ia::getDicoFormes()
{
        return dicoFormes;
}


Dico * Ia::getDicoJoseki()
{
        return dicoJoseki;
}


void Ia::reinittable(vector<vector<int> > * table)
{
        for (int i=0; i<table->size(); i++)
                for (int j=0; j<(*table)[0].size(); j++)
                        (* table)[i][j]=0;
}

void Ia::afficherPoids()
{
        cout<<"**Tableau des Poids **"<<endl;
        cout<<"       0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18"<<endl;
        for (int i =0; i< Poids.size(); i++) {
           if (i<10) cout<<i<<": ";
           else cout<<i<<":";
                for (int j =0;j<Poids[i].size();j++) {
                        //cout<<" - "<<"("<<i<<","<<j<<")";
                        cout<<"|";
                        if (Poids[i][j]>=0){cout<<" ";}
                        if (Poids[i][j]<10&&Poids[i][j]>=0){cout<<"  ";}
                        else if (Poids[i][j]<100&&Poids[i][j]>=0||Poids[i][j]>-100&&Poids[i][j]<0){cout<<" ";}

                        cout<<Poids[i][j];
                }
                cout<<endl;
        }
        cout<<"________________________________________________________________________________________________________"<<endl;
}



void Ia::affichetable(vector<vector<int> > * table)
{
        for (int i=0; i<table->size(); i++) {
                for (int j=0; j<(* table)[0].size(); j++) {
                        cout<<(* table)[i][j];
                        if ((* table)[i][j]<10 && (* table)[i][j]>=0) cout<<"  . ";
                        else if ((* table)[i][j]>-10)  cout<<" . ";
                        else cout<<". ";
                }
                cout<<endl;
        }
}



void Ia::majpoids(vector <vector<int> > * table, int x, int y, vector<vector<int> > * tablePoids)
{
        cout<<"majpoids"<<endl;
        cout<<x<<endl;
        cout<<y<<endl;
        //CalculerMeilleurCoup(); //TRACE
        //affichetable(table);
        Poids[x][y]=-99;
        for (int i=0; i<(* table).size(); i++) {
                for (int j=0; j<(* table)[0].size(); j++) {
                        if ((* table)[i][j] !=0) {
                                //   cout<<"-----valeur : "<<(* tablePoids)[i+x-((* table).size()-1)/2][j+y-((* table).size()-1)/2]<<endl;
                                if (p->dansPlateau(i+x-((* table).size()-1)/2, j+y -((* table).size()-1)/2)) {
                                        (* tablePoids)[i+x-((* table).size()-1)/2][j+y-((* table).size()-1)/2]+=(* table)[i][j];
                                        //   cout<<"-----ds goban : "<<i+x-((* table).size()-1)/2<<" - "<< j+y -((* table).size()-1)/2<<endl;
                                        //   cout<<"-----nv valeur : "<<(* tablePoids)[i+x-((* table).size()-1)/2][j+y-((* table).size()-1)/2]<<endl;
                                } else {
                                        // cout<<"-----PAS DANS GOBAN!"<<i<<" -"<<j<<" : "<<x<<" - "<<y<<"taille : "<<(* table).size()<<endl;
                                }
                        }
                }
        }
        cout<<"fin maj poid"<<endl;
        // CalculerMeilleurCoup(); //TRACE
}

void * iaDescendre(void * arbre) {

  /*  cout<<"IADESSENDRE 1"<<endl;
    ((arbreUCT*)arbre)->setlisteCustomBlanc(new listeChaineeUCT(((arbreUCT*)arbre)->getlisteChaineeBlanc()));
    ((arbreUCT*)arbre)->setlisteCustomNoir(new listeChaineeUCT(((arbreUCT*)arbre)->getlisteChaineeNoir()));
    cout<<"IADESSENDRE 2"<<endl; */
	((arbreUCT*)arbre)->dessente2(nbd);

}

void Ia::threadLauncher() {
    cout<<"lancement des thread : "<<arbres.size()<<endl;
    for (int i =0; i< arbres.size(); i++) {
        pthread_create(&thread[i], NULL,iaDescendre, (void*) arbres[i]); //lancement en parallele de descendre
    }
    //cout<<"attente de la liberation des threads"<<endl;
    for (int i =0; i<thread.size(); i++) {
        cout<<"pre join"<<endl;
        pthread_join(thread[i], NULL);   // attente de la fi des threads pour les relancer
        cout<<"post join"<<endl;
    }
    //cout<<"threads finis"<<endl;
}




//La fonction qui joue.
Coup Ia::jouer()
{
        cout<<"Debut de la reflexion"<<endl;
       //on commence par calculer les formes de toutes les pierres.
       /*
		reinittable(&Poids);
        vector<vector<int> > listeCoups; // la liste des coups voisins pour chaque pierre.
        listeCoups.resize(2);
        vector<vector<int> > PoidsdesFormes; //les poids générés par les formes.
        //les formes sont calculées sur du 9*9
        PoidsdesFormes.resize(9);
//		cout<<"Coup Ia::jouer() avant premier for"<<endl;
        for (int i=0; i<PoidsdesFormes.size();i++) {
                PoidsdesFormes[i].resize(9);
        }
//		cout<<"Coup Ia::jouer() apres premier for, avant second for"<<endl;
//on parcourt tous les coups qui ont été joués
        for (int i=0;i<g->getHistorique().size();i++) {
            if(g->getHistorique()[i].x>-1){ //pas de passe ou marques spéclaies
                reinittable(&PoidsdesFormes);
                listeCoups[0].resize(0);
                listeCoups[1].resize(0);
    //			cout<<"Coup Ia::jouer() début second for"<<endl;
                Intersection * pierre = g->getPierre(g->getHistorique()[i].x,g->getHistorique()[i].y);

    //			cout<<"Coup Ia::jouer() g->getHistorique()[i].x : "<<(int) g->getHistorique()[i].x<<" - g->getHistorique()[i].y : "<<(int) g->getHistorique()[i].y<<endl;
    //			cout<<"Coup Ia::jouer() avant premier sous-for"<<endl;
    //			//on va chercher les coups de la forme et on rajoute un pass si le dernier coup est de notre couleur
                cout<<"taille : "<<pierre->getListeCoups()[0].size()<<endl;
                for (int j=0;j<pierre->getListeCoups()[0].size();j++) {
                        cout<<"Coup :"<<pierre->getListeCoups()[0][j]<<" - "<<pierre->getListeCoups()[1][j]<<endl;
                        listeCoups[0].push_back(pierre->getListeCoups()[0][j]);
                        listeCoups[1].push_back(pierre->getListeCoups()[1][j]);
                }
                //if (g->getIntersection(pierre->getListeCoups()[0].back() + pierre->getX(), pierre->getListeCoups()[1].back() + pierre->getY() )->getType()== couleur) {
                if(pierre->getType()!=couleur){
                        //on rajoute un pass pour que ça soit à l'IA de jouer
                        listeCoups[0].push_back(-100);
                        listeCoups[1].push_back(-100);
                        dicoFormes->chercherPosition(& listeCoups, & PoidsdesFormes, false);

                } else {
                    dicoFormes->chercherPosition(& listeCoups, & PoidsdesFormes, true);
                }
    //recherche dans le dico.
                cout<<"affichage des poids induits par les formes de la pierre"<<pierre->getX() <<" - "<<pierre->getY()<<endl;
                affichetable(&PoidsdesFormes);
                majpoids(&PoidsdesFormes, pierre->getX(), pierre->getY(), &Poids );
            }
        }
//		cout<<"Coup Ia::jouer() après second for"<<endl;

        cout<<"affichage des poids GLOBAUX apres l'appel des formes et avant l'appel des regles"<<endl;
        afficherPoids();

        //application des regles:
        mesRegles->initFait();
		mesRegles->executerRegles();
		mesRegles->executerConcl();
        cout<<"Ia::Jouer() on vient d'executerConcl()"<<endl;

        //majpoids(mesRegles->getPoids(), mesRegles->abscisseTengen(), mesRegles->abscisseTengen(), &Poids );

        cout<<"on vient d'appliquer les regles, Machiabot est content."<<endl;
*/
        int PoidsMax= -1000;


        Coup CoupdeDieu;

        /*
        CoupdeDieu.c=couleur;

        listeChaineeNoir.divise(4);
        listeChaineeBlanc.divise(2);

        Coup cTemp;
        for (int i=0; i<Poids.size();i++) {
                for (int j=0; j<Poids[i].size();j++) {
                        if(g->getIntersection(i, j)->couleur() != VIDE)
                            Poids[i][j] = -99;
                        else
                            Poids[i][j]= (POIDS_FORMES * Poids[i][j] ) + (POIDS_REGLES * (* mesRegles->getPoids())[i][j]);
                        //listeChaineeBlanc.addMaillonTrick();
                        cTemp.x = i;
                        cTemp.y = j;
                        cTemp.c = NOIR;
                        if(Poids[i][j]!=0){
                            cout<<"Trick UCT avec"<<i<<" - "<<j<<" à hauteur de : "<<Poids[i][j]<<endl;
                            listeChaineeNoir.addMaillonTrick(cTemp,Poids[i][j]);

                        //VERSION TEMP : a remplacer des que listeChainée sera partagé entre les threads.
                           // for(int n =0; n<arbres.size(); n ++){
                             //   arbres[n]->trickRave(cTemp, Poids[i][j]);

                            //}
                        }
                        if (Poids[i][j]>PoidsMax) {
                                PoidsMax=Poids[i][j];
                                CoupdeDieu.x=i;
                                CoupdeDieu.y=j;
                        }
                }
        }
        cout<<"affichage des poids GLOBAUX apres l'appel des REGLES"<<endl;
        afficherPoids();
        cout<<endl<<"Le système d'ingeniere cognitive a choisi le coup : "<<(int)CoupdeDieu.x<<" - "<<(int)CoupdeDieu.y<<endl;
*/
        int taille = p->getTaille();
      //  if( taille ==9 || taille ==13){ // ON RENTRE DANS UCT!

            Coup coupSave = CoupdeDieu;
            cout<<"liste pre UCT : "<<endl;
        //    listeChaineeNoir.afficherListeMaillons();
        //    listeChaineeBlanc.afficherListeMaillons();
            //listeChainee.afficherListeMaillons();
            cout<<"lancement d'UCT!"<<endl;
            threadLauncher();
            cout<<"UCT fini"<<endl;
          //  listeChaineeBlanc.afficherListeMaillons();
          //  listeChaineeNoir.afficherListeMaillons();
            //listeChainee->afficherListeMaillons();
            //arbres[0]->getlisteCustomBlanc()->afficherListeMaillons();
            //arbres[0]->getlisteCustomNoir()->afficherListeMaillons();
            //listeChaineeNoir.afficherListeMaillons();
            cout<<"affichage termine"<<endl;
            Coup cr = arbres[0]->meilleurFils();
            cout<<"coup retenu par UCT : piece: "<<cr.p.numero<<" : "<<(int)cr.place.x<<" - "<<(int)cr.place.y<<endl;
            vector< vector <double> > vecPoidsUCT;
            vecPoidsUCT.resize(taille);
            for(int i =0; i<taille; i++){
                vecPoidsUCT[i].resize(taille);
            }
            for(int i=0; i<taille; i++){
                for(int j=0; j<taille; j++){
                    vecPoidsUCT[i][j]=0;
                }
            }
            //majtable(vector<vector <double> > * table);
            arbres[0]->majtable(&vecPoidsUCT);
         /*   cout<<"tableau poids"<<endl;
            cout<<Poids.size()<<endl;

            for (int i=0; i<Poids.size();i++) {
                    for (int j=0; j<Poids[i].size();j++) {
                            Poids[i][j] += POIDS_UCT * vecPoidsUCT[i][j]  ; //+ (POIDS_REGLES * (* mesRegles->getPoids())[i][j]);
                            if (Poids[i][j]>PoidsMax) {
                                    PoidsMax=Poids[i][j];
                                    CoupdeDieu.place.x=i;
                                    CoupdeDieu.place.y=j;
                            }
                    }
            }
            afficherPoids();
            cout<<endl<<"L'ia finale a choisi : "<<(int)CoupdeDieu.place.x<<" - "<<(int)CoupdeDieu.place.y<<" ,piece n° "<<CoupdeDieu.p.numero<<endl;
            if(!p->coupJouable(CoupdeDieu) ){
                CoupdeDieu.place.x= -100;
                CoupdeDieu.place.y= -100;
            }*/
        /*   if( (! g->caseJouableUCT(CoupdeDieu) ) && PoidsMax<poidsUCT*2){
                bool passer = false;
                for (int i=0; i<taille && !passer; i++)
                    for (int j=0; j<taille && !passer; j++)
                        if(g->caseJouableUCT(i,j,couleur))
                            passer= true;
            } */

        /*    if(PoidsMax<poidsUCT*1.5){
                CoupdeDieu.place.x= -101;
                CoupdeDieu.place.y= -101;
            }
            arbres[0]->traceFils(nbRepet);
            arbres[0]->traceRave();   */
            cout<<"juste avant le allerA"<<endl;

            arbres[0]->allerA(cr);
            for (int i = 1; i <arbres.size(); i++) {
                arbres[i]->setNoeudRacine(arbres[0]->getNoeudRacine());
            }
            if(coupSave.place.x == CoupdeDieu.place.x && coupSave.place.y == CoupdeDieu.place.y){
                poidsUCT+=5;
            }
            //nbRepet = nbRepet * 0.98;
            poidsDicoFormes = poidsDicoFormes * 0.95;
            nbd = nbRepet;
            cout<<"poids UCT maintenant : "<<poidsUCT<<endl;
            cout<<"poids Dico Formes maintenant : "<<poidsDicoFormes<<endl;
            cout<<"nbRepet : "<<nbRepet<<endl;
            cout<<"poidMax : "<<PoidsMax<<endl;
       /* } else {
            if(!g->caseJouable(CoupdeDieu) ){
                CoupdeDieu.x= -101;
                CoupdeDieu.y= -101;
            }
        } */
        return cr;
}
//la fonction qui joue jusqu'a ce qu'elle ne puisse plus.
void Ia::jouerEnBoucle(){
    int i = 0;
	while (!arbres[0]->getNoeudRacine()->getScore()){
		if (p->getNbPiecesPosees() == p->getTaille()*p->getTaille()){
			cout<<"WIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIINNNNNNNNNNNNNNNNN!!!!!!"<<endl;
			sauvegarde(p, 20);
			exit(0);
		}
		else{
		    if(i % 5 == 0){
                sauvegarde(p, i%5);
		    }
		    i++;
		    cout<<"AFFICHAGE IA ! "<<endl;
            p->affichePlateau();
			p->jouer(jouer());

		}
	}
	cout<<"sortie du while... On a perdu..."<<endl;
    sauvegarde(p, 21);
}



void Ia::trickRave(){
//A MUTEXER
    //listeCustomNoir->raz();
  /*  Coup c;
        for(int i = 0; i < p->getTaille(); i ++){
            for(int j = 0; j < p->getTaille(); j ++){
                //1000/ (fabs(i-4) + fabs(j-4));
                c.place.x = i;
                c.place.y = j;
                //if(gobanActuel->caseJouableUCT(i,j,NOIR)){
                   listeChaineeNoir.addMaillonTrick(c, g->getTaille()*g->getTaille()*2/ ((i-g->getTaille()/2) * (i-g->getTaille()/2) + (j-g->getTaille()/2) * (j-g->getTaille()/2) + 1) );
                    listeChaineeBlanc.addMaillonTrick(c, g->getTaille()*g->getTaille()*2/ ((i-g->getTaille()/2) * (i-g->getTaille()/2) + (j-g->getTaille()/2) * (j-g->getTaille()/2) + 1) );
                //}
            }
        }*/
}

